﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Text;
using System.Web;
using System.Collections.Specialized;
using Peninsula.Utility.Extensions;

namespace Peninsula.Utility.HttpUtil
{
    /// <summary>
    /// Writed by Rui Zhong at 2013.05.16
    /// </summary>
    public class Http
    {
        #region Fields

        private readonly static int timeout;
        private readonly static int readWriteTimeout;

        #endregion

        #region Properties

        public HttpStatusCode ErrorResponseCode { private set; get; }
        public string ErrorDescription { private set; get; }

        public HttpWebResponse RequestResponse { private set; get; }
        public HttpStatusCode ResponseCode { private set; get; }

        public string TextResponse { private set; get; }
        public byte[] BinaryResponse { private set; get; }

        #endregion
        
        static Http()
        {
            timeout = ConfigurationManager.AppSettings["timeout"] == null ? 10000 : int.Parse(ConfigurationManager.AppSettings["timeout"]);
            readWriteTimeout = ConfigurationManager.AppSettings["readWriteTimeout"] == null ? 10000 : int.Parse(ConfigurationManager.AppSettings["readWriteTimeout"]);
        }

        /// <summary>
        /// Sent post request
        /// </summary>
        /// <param name="url"></param>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public bool HttpPost(string url, NameValueCollection nvc = null, string postBody = null,Encoding encoding = null )
        {
            StringBuilder sbData;

            try
            {
                // Create POST request
                HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
                r.Method = "POST";
                r.ContentType = "application/x-www-form-urlencoded";
                r.Timeout = timeout;
                r.ReadWriteTimeout = readWriteTimeout;
                r.KeepAlive = false;
                r.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);

                // Build POST query
                sbData = new StringBuilder();
                int i = 0;
                if (nvc != null)
                {
                    foreach (string key in nvc.Keys)
                    {
                        string s = (i++ == 0) ? "" : "&";
                        sbData.Append(s + key + "=" + HttpUtility.UrlEncode(nvc[key]));
                    }
                }
                else if (!string.IsNullOrWhiteSpace(postBody))
                {
                    sbData.Append(postBody);
                }

                if (encoding == null)
                {
                    encoding = UTF8Encoding.UTF8;
                }
                // Write data  
                byte[] byteData = encoding.GetBytes(sbData.ToString());
                r.ContentLength = byteData.Length;
                using (Stream postStream = r.GetRequestStream())
                {
                    postStream.Write(byteData, 0, byteData.Length);
                }

                return GetResponse(r);
            }
            catch (WebException wex)
            { 
                if (wex.Response != null)
                {
                    using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
                    {
                        ErrorResponseCode = errorResponse.StatusCode;
                        ErrorDescription = errorResponse.StatusDescription;
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorDescription = ex.Message;
            }

            return false;
        }

        /// <summary>
        /// Sent get request
        /// </summary>
        /// <param name="url"></param>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public bool HttpGet(string url, NameValueCollection nvc = null)
        {
            try
            {
                if (nvc != null)
                {
                    string querySring = "";

                    foreach (string key in nvc.Keys)
                    {
                        querySring += string.Format("&{0}={1}", key, nvc[key]);
                    }

                    if (url.IndexOf("?") > 0)
                    {
                        url += querySring;
                    }
                    else
                    {
                        url = url + "?" + querySring.Substring(1);
                    }
                }

                // Create POST request
                HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
                r.Method = "GET";
                r.Timeout = timeout;
                r.ReadWriteTimeout = readWriteTimeout;
                r.KeepAlive = false;
                r.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
                r.AllowWriteStreamBuffering = false;

                return GetResponse(r);
            }
            catch (WebException wex)
            {
                if (wex.Response != null)
                {
                    using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
                    {
                        ErrorResponseCode = errorResponse.StatusCode;
                        ErrorDescription = errorResponse.StatusDescription;
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorDescription = ex.Message;
            }

            return false;
        }

        /// <summary>
        /// post filed to api
        /// </summary>
        /// <param name="url"></param>
        /// <param name="filepath"></param>
        /// <param name="paramName"></param>
        /// <param name="contentType"></param>
        /// <param name="nvc"></param>
        /// <returns></returns>
        public bool HttpPostFile(string url, string filepath, string paramName, string contentType, NameValueCollection nvc = null)
        {
            try
            {
                string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
                byte[] boundarybytes = System.Text.Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n");

                HttpWebRequest r = (HttpWebRequest)WebRequest.Create(url);
                r.ContentType = "multipart/form-data; boundary=" + boundary;
                r.Method = "POST";
                r.KeepAlive = true;
                r.Credentials = System.Net.CredentialCache.DefaultCredentials;

                Stream rs = r.GetRequestStream();

                if (nvc != null)
                {
                    string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";
                    foreach (string key in nvc.Keys)
                    {
                        rs.Write(boundarybytes, 0, boundarybytes.Length);
                        string formitem = string.Format(formdataTemplate, key, nvc[key]);
                        byte[] formitembytes = System.Text.Encoding.UTF8.GetBytes(formitem);
                        rs.Write(formitembytes, 0, formitembytes.Length);
                    }
                }

                rs.Write(boundarybytes, 0, boundarybytes.Length);

                string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
                string header = string.Format(headerTemplate, paramName, filepath, contentType);
                byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
                rs.Write(headerbytes, 0, headerbytes.Length);

                FileStream fileStream = new FileStream(filepath, FileMode.Open, FileAccess.Read);
                byte[] buffer = new byte[16384];
                int bytesRead = 0;
                while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
                {
                    rs.Write(buffer, 0, bytesRead);
                }
                fileStream.Close();

                byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
                rs.Write(trailer, 0, trailer.Length);
                rs.Close();

                return GetResponse(r);
            }
            catch (WebException wex)
            {
                if (wex.Response != null)
                {
                    using (HttpWebResponse errorResponse = (HttpWebResponse)wex.Response)
                    {
                        ErrorResponseCode = errorResponse.StatusCode;
                        ErrorDescription = errorResponse.StatusDescription;
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorDescription = ex.Message;
            }

            return false;
        }

        private bool GetResponse(HttpWebRequest r)
        {
            // Get RequestResponse
            RequestResponse = (HttpWebResponse)r.GetResponse();

            TextResponse = null;
            BinaryResponse = null;

            if (RequestResponse.ContentType.Contains("text"))
            {
                // Get an Text response
                StreamReader reader = new StreamReader(RequestResponse.GetResponseStream(), Encoding.UTF8);
                StringBuilder writer = new StringBuilder(reader.ReadToEnd());
                TextResponse = writer.ToString();
            }
            else
            {
                // Get a binary response
                MemoryStream m = new MemoryStream();
                if (r.HaveResponse && (RequestResponse != null))
                {
                    byte[] buffer = new byte[16384];
                    int bytesread;

                    BinaryReader reader = new BinaryReader(RequestResponse.GetResponseStream());
                    BinaryWriter writer = new BinaryWriter(m);
                    while (true)
                    {
                        bytesread = reader.Read(buffer, 0, buffer.Length);
                        if (bytesread == 0)
                            break;

                        writer.Write(buffer, 0, bytesread);
                    }
                    writer.Close();
                }

                BinaryResponse = m.ToArray();
                m.Close();
            }

            ResponseCode = RequestResponse.StatusCode;
            RequestResponse.Close();

            return true;
        }
    }
}
